Repository navigation
serve: name the HSA runtime HRX loads, path and sha256 (engine#713 criterion 4/5) - #888
Conversation
The engine#713 bootstrap (the pinned toolchain under the tree) landed in #771. What was still missing from that issue's acceptance list is criterion 4: HRX resolved the libhsa-runtime64.so.1 it would dlopen and handed it to llama-server, but never said which file it was. Three exist on a box and they are not interchangeable - the release-shipped one does not register HRX0 for a dev build - so a run could silently fall back to another copy. app/hrx_runtime.h carries the tree's one SHA-256 implementation (tests/npu_q4nx_test.cpp now shares it instead of keeping its own), the file digest, and the line; serve prints it once at the first HRX backend, before the CWSR decision line, and docs/releases.md records the pinned runtime's sha256 and its measured gfx1151 capability lines next to the toolchain pin (criterion 5). Tests, both without a GPU: tests/hrx_runtime (NIST vectors, a real file against its bytes, a missing file, both line forms) and tests/hrx_runtime_route (a fake runtime file through serve: the line and its digest checked with sha256sum, the IREE_HAL_AMDGPU_LIBHSA_PATH the backend receives, an unreadable path named without a fabricated digest, and --device cpu printing nothing).
PR Reviewer Guide 🔍(Review updated until commit c507e90)Here are some key observations to aid the review process:
|
The CI build lists every target a ctest test invokes (the comment above it says why: ctest runs whatever binaries exist). engine#713's new device-free test was missing from the list, so the first run of PR #888 failed with "32 - hrx_runtime (Not Run)".
|
Thanks — two answers, and the first one is a real (if cosmetic) point.
SHA-256 in a hot path: it runs once per |
|
Persistent review updated to latest commit ebacdfa |
1 similar comment
|
Persistent review updated to latest commit ebacdfa |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY
At ebacdfa6e (BEHIND main by merges only, no conflict). Built and ran everything the PR claims, plus the arithmetic.
Built and run at this head (cmake -DCMAKE_BUILD_TYPE=Release, targets onebit hrx_runtime_test):
./hrx_runtime_test -> hrx_runtime: all checks passed
ctest -R '^hrx_runtime$' -> 1/1 Passed (registered, not Not Run)
tests/hrx_runtime_route.sh ./build/1bit -> 8/8 ok, rc 0
ok the backend gets the runtime serve named
ok serve names the runtime and its sha256
ok the digest is the file's, checked with sha256sum
ok announced exactly once
ok missing: the line names the path and says it is unreadable
ok missing: no fabricated digest
ok --device cpu: no runtime line
ok --device cpu: no IREE_HAL_AMDGPU_LIBHSA_PATH in the backend's env
The SHA-256 against coreutils, on inputs bigger than the FIPS vectors. The vectors pin single-block and 56-byte cases; a long-length encoding bug would survive them, and this digest is now printed on every HRX serve, so I hashed real files through onebit::sha256_file() and compared with sha256sum:
21a6682452e159cfcdc6bdc84676f6b7f9c1236f3a2957a97a2e7170889cdfa3 libhsa-runtime64.so.1 (5,141,585 B) both agree
13e33455f91cca68760e18e7f1ccff1455834ca2065a9c36f802e6e627ba6af5 Qwen3-4B model.onnx (2.2 GB) both agree
The pin in docs/releases.md is the real file, and the three-runtime premise is not hypothetical — every copy on this box:
5,141,585 f5462dc36b0a… release-rc/work/os/data.dir/1bit-llama/libhsa-runtime64.so.1 (same size, different bytes)
5,141,585 21a6682452e1… rocm-sdk-10.3.0a20261031-gfx1151/…/_rocm_sdk_core/lib/… = the documented pin
4,957,049 f6ef689d163b… release-shipped /opt/rocm-therock/lib/… (c16 squashfs skeleton)
The documented size and full digest match the pinned tree byte for byte, and there really are three files called the same thing that are not the same file.
The capability row, reproduced with the pinned tree's own rocminfo under gpu-hold -l "pi prs 888" -t 20: Runtime Version 1.22, System Timestamp Freq.: 1000.000000MHz, XNACK enabled: NO, Coherent Host Access: FALSE, Memory Properties: APU, gfx1151 / AMD Radeon 8060S Graphics. Every field in the new table is what the tool prints.
Checked by reading, since they decide when the line appears: all three call sites are HRX-only (launch_for under device == "hrx", rag_launch under the same, serve_child under !dev || dev == "hrx"), the line comes from the same hrx_libhsa() call that fills IREE_HAL_AMDGPU_LIBHSA_PATH, and serve_child is called in-process (app/serve.cpp:2970), so the static bool guard means once per process rather than once per fork. CI's explicit target list gains hrx_runtime_test (ci.yml:77), which is the #635 trap and it is handled.
One nit, worth one line: sha256_hex() does std::vector<uint8_t> m(data, data + len), so the empty-file case (sha256_file() passes bytes.data(), which may be null for an empty vector) forms nullptr + 0. It returns the right digest on libstdc++ and the runtime is never empty, but an early if (!len) return "e3b0c442…"; or a one-line guard keeps a UBSan run quiet.
|
#888 ready for review at e39b3fb ( What I re-checked at the merged head:
|
|
Persistent review updated to latest commit e39b3fb |
|
Persistent review updated to latest commit c507e90 |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at c507e90ed — a merge of eight main commits into the head I verdicted. Seven of this PR's nine files are blob-identical; CMakeLists.txt and docs/hrx.md differ, and the difference is main's, not this PR's: hrx_cpu_weight_parity + the hrx-env.sh IREE_HAL_AMDGPU_LIBHSA_PATH recipe (#883) and npu_window11_policy (#880). Every hunk this PR adds is still present at the new head — add_executable(hrx_runtime_test), add_test(NAME hrx_runtime …), hrx_runtime_route, the documented pin 21a6682452e159cf… in docs/releases.md, and announce_hrx_runtime's four call-path references — and the PR's own diff is the same nine files, +339/−41.
|
#888 ready for review at c507e90 The waiter merged
The earlier |
The bootstrap itself landed in #771 (
a3f64ec50); this PR closes the part of the issue's own acceptance list that wasstill missing — criterion 4 (the HRX init line names the HSA runtime, path and sha256) and criterion 5 (the
release table carries the toolchain pin with that sha256 and the measured capability lines) — and records the
measurements for the rest.
closes #713
Why the runtime has to be named (criterion 4)
Three
libhsa-runtime64.so.1exist on a box and they are not interchangeable (the issue's residual table): therelease-shipped one does not register
HRX0for a dev build, the pinned toolchain's does, a distro one isuntested. Before this PR
serveresolved the path (hrx_libhsa()) and handed it to llama-server but never said whichfile it was, so a silent fallback could pass a run while a different runtime executed — which is exactly why the
engine#315 residual A/B was trustworthy: every arm named its runtime.
What changed
app/hrx_runtime.h(new, header only): the tree's one SHA-256 (FIPS 180-4) implementation — lifted fromtests/npu_q4nx_test.cpp, which now includes it instead of carrying its own copy — plussha256_file()and the lineHRX: HSA runtime <path> sha256 <hex>, with… (unreadable: no sha256, HRX may fail to open it)when the filecannot be read (HRX still tries to
dlopenit, so the log has to name it).app/serve.cpp:announce_hrx_runtime()besideannounce_cwsr_env()at all three HRX backend sites (chatserver, role/RAG server, Laya daemon), once per process, before the CWSR decision line.
tests/hrx_runtime_test.cpp(NIST vectors — empty,"abc", the 56-byte two-blockmessage — a real file against its bytes, a missing file as
"", both line forms) andtests/hrx_runtime_route.sh(end to end: a fake runtime file through
serve, so the line names it withsha256sum's digest exactly once, thebackend's
IREE_HAL_AMDGPU_LIBHSA_PATHis that same path, an unreadable path is named without a fabricated digest,and
--device cpuprints no runtime line at all).docs/hrx.md"The HSA runtime" gained the line and how to check it;docs/releases.mdgained the pinnedruntime's size/sha256 and its measured capability lines in a table beside the pins (criterion 5).
Measured on Strix Halo (RYZEN AI MAX+ 395 / Radeon 8060S,
gfx1151)Box state for every row:
origin/main(23d6972) with its pinned submodules, the pinned toolchain fetched intothird_party/toolchain/rocm-sdk-10.3.0a20261031-gfx1151,/opt/rocm-therockmade an empty tmpfs inside a privatemount namespace (so nothing on the box changed and no run could use it), no
ROCM_PATH/HIP_PATH/HIP_DEVICE_LIB_PATH/-DONEBIT_HRX_TOOLCHAIN.ONEBIT_HRX: toolchain …/third_party/toolchain/rocm-sdk-10.3.0a20261031-gfx1151, and the same copy for the HSA runtime and theamdgcndevice library;cmake --build --target onebit llama_hrxrc 0,test-backend-opsrc 0HRX0up on a dev build)1bit serve -m Qwen3-0.6B-Q4_K_M.gguf --device hrx(defaults, no libhsa override) answered the warm-up and a completion with HTTP 200; decode 256 tok/s, prefill 73 tok/s1bit serve: HRX: HSA runtime …/rocm-sdk-10.3.0a20261031-gfx1151/…/_rocm_sdk_core/lib/libhsa-runtime64.so.1 sha256 21a6682452e159cfcdc6bdc84676f6b7f9c1236f3a2957a97a2e7170889cdfa3— identical tosha256sumof that path (5,141,585 B), the digest the issue's residual table lists for TheRock's nightlytest-backend-ops test -b HRX0 -o TOP_K: 484/484 tests passed,Backend HRX0: OK,2/2 backends passed, zero fault linesgfxhub … page faultcount indmesg0 before, 0 after both runs above/opt/rocm-therock)/opthidden as above:scripts/weekly.sh <W> buildgets past preflight and resolves hipcc from the tree copy (HIP version 7.18.26434-0000000,AMD clang 24.0.0git). With the bootstrap unable to complete (no wheel index):preflight: cannot fetch the pinned HRX toolchain (config/hrx-toolchain.config)thenpreflight failed: nothing was run, rc 1 — no fallback to a box-local runtime.scripts/fetch-rocm-sdk.sh --checkis rc 0 at the pin, rc 1 for a missing tree and for a tree at another pindocs/releases.md: the toolchain row carries the runtime digest, and a table below it records 5,141,585 B /21a66824…andXNACK enabled: NO,Coherent Host Access: FALSE,Memory Properties: APU, Runtime Version 1.22 (measured with the pinned tree's ownrocminfo,/opthidden)Not measured: criterion 7's byte-for-byte comparison (
llama-server --versionand the embedded ggml commit) against asecond build of the same tree made with
-DONEBIT_HRX_TOOLCHAIN=/opt/rocm-therock. The build was made and run from therepository alone, which is the substance of the criterion; the diff of the two binaries is a follow-up.
Notes
finish_reason: "length"for Qwen3-0.6B: this model's chat template puts theanswer in
reasoning_contentand serve's chat-format parser rejects it in the warm-up (a pre-existing template quirk,unrelated to this change); the HTTP 200 and the JSON envelope are what criterion 1 asks for.